﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using Cms.Domain;
using Cms.Domain.RepositoryInterfaces;

namespace Cms.Application
{
    public class PluginHandler
    {
        public static void SyncPlugins(ISystemRepository systemRepository)
        {
            Assembly webFrameworkAssembly = Assembly.Load("Cms.Web.Framework");
            SyncPluginsInAssembly(webFrameworkAssembly, systemRepository, webFrameworkAssembly);
            string[] customPluginAssemblies =
                ConfigurationManager.AppSettings["CustomPluginAssemblies"].Split(",".ToCharArray());
            foreach (string customPluginAssembly in customPluginAssemblies)
            {
                if (customPluginAssembly.Trim() == "")
                    continue;
                Assembly a = Assembly.Load(customPluginAssembly.Trim());
                SyncPluginsInAssembly(a, systemRepository, webFrameworkAssembly);
            }
        }

        private static void SyncPluginsInAssembly(Assembly a, ISystemRepository systemRepository, Assembly webFrameworkAssembly)
        {
            IList<Plugin> assemblyPlugins = new List<Plugin>();
            Plugin plugin;
            foreach (Type t in a.GetTypes())
            {
                if (t.IsClass && !t.IsAbstract &&
                    t.IsSubclassOf(webFrameworkAssembly.GetType("Cms.Web.Framework.Plugins.BasePlugin", true, true)))
                {
                    plugin =
                        t.IsSubclassOf(webFrameworkAssembly.GetType("Cms.Web.Framework.Plugins.BaseTemplatedPlugin",
                                                                    true, true))
                            ? new TemplatablePlugin()
                            : new Plugin();
                    plugin.FullClassName = t.FullName;
                    plugin.AssemblyName = a.GetName().Name;
                    plugin.Activated = true;
                    assemblyPlugins.Add(plugin);
                }
            }

            // Remove plugins from the database if they can not be found in the assemblies
            IList<int> deleteIDs = new List<int>();
            foreach (Plugin dbPlugin in systemRepository.GetPlugins())
            {
                bool found = false;
                foreach (Plugin assemblyPlugin in assemblyPlugins)
                {
                    if (dbPlugin.FullClassName == assemblyPlugin.FullClassName)
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                    deleteIDs.Add(dbPlugin.ID);
            }
            foreach (int id in deleteIDs)
                systemRepository.DeletePlugin(id);

            // Add new plugins to the database
            foreach (Plugin assemblyPlugin in assemblyPlugins)
            {
                bool found = false;
                foreach (Plugin dbPlugin in systemRepository.GetPlugins())
                {
                    if (dbPlugin.FullClassName == assemblyPlugin.FullClassName)
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                    systemRepository.SavePlugin(assemblyPlugin);
            }
        }
        
        public static IList<PropertyInfo> GetPluginProperties(int pluginID, ISystemRepository systemRepository)
        {
            Plugin plugin = systemRepository.GetPlugin(pluginID);
            Assembly webFrameworkAssembly = Assembly.Load("Cms.Web.Framework");
            Type basePlugin = webFrameworkAssembly.GetType("Cms.Web.Framework.Plugins.BasePlugin", true, true);
            Type baseTemplatedPlugin = webFrameworkAssembly.GetType("Cms.Web.Framework.Plugins.BaseTemplatedPlugin",
                                                                    true, true);
            Assembly a = Assembly.Load(plugin.AssemblyName);
            Type t = a.GetType(plugin.FullClassName);
            IList<PropertyInfo> properties = new List<PropertyInfo>();
            do
            {
                foreach (
                    PropertyInfo pi in
                        t.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance))
                {
                    if (pi.CanWrite &&
                        !((pi.Name == "TemplateID" | pi.Name == "TemplateInstance") & t == baseTemplatedPlugin))
                        properties.Add(pi);
                }
                t = t.BaseType;
            } while (t != basePlugin);
            return properties;
        }
    }
}